package com.googlecode.smartgwtmavenplugin.mojo;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.NotFileFilter;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.AntPathMatcher;

import com.googlecode.smartgwtmavenplugin.Archive;
import com.googlecode.smartgwtmavenplugin.JarBuilder;
import com.googlecode.smartgwtmavenplugin.PluginContextKeyConstants;

/**
 * Unzips the Isomorphic build/s where necessary (i.e., for .zip files), and performs a copy otherwise (.jar) files.  Also installs
 * POM resources from the classpath and bundles javadoc documentation into JAR files suitable for deployment with a Maven artifact.
 * 
 * @goal prepare
 * @execute goal=download
 * @requiresProject false
 * @requiresDirectInvocation true
 */
public class PrepareMojo extends AbstractMojo {

	/** @parameter property="workdir.download" */
	private File downloaded;

	/** @parameter property="workdir.unpacked" */
	private File unpacked;
	
	/** @parameter property="workdir.latest" */
	private File latest;

	/** @parameter property="version" */
	private String version;
	
	/** @parameter property="overwrite.poms" default-value="false*/
	private boolean overwrite;
	
	/**
	 * 
	 */
	public void execute() throws MojoExecutionException, MojoFailureException {

		Set<Archive> archives = (Set<Archive>) getPluginContext().get(PluginContextKeyConstants.ARCHIVE_SET);
		for (Archive archive : archives) {
			File file = new File(downloaded, archive.getLocalFileName());
			try {
				if (file.getName().endsWith(".zip")) {
					getLog().info("Unpacking '" + file.getAbsolutePath() + "' to '" + unpacked.getAbsolutePath() + "'");
					ZipFile zip = new ZipFile(file);
					unpack(archive, zip);
				} else {
					getLog().info("Copying '" + file.getAbsolutePath() + "' to '" + unpacked.getAbsolutePath() + "'");
					FileUtils.copyFileToDirectory(file, unpacked);
				}
			} catch (IOException e) {
				throw new MojoExecutionException("Error processing file '" + file.getAbsolutePath() + "'", e);
			}
		}

		File doc = new File(unpacked, "doc");
		try {
			File client = new File(doc, "javadoc");
			if (client.exists() && client.isDirectory()) {
				getLog().info("Repackaging client javadoc...");
				JarBuilder.build(client, new File(doc, "javadoc-client.jar"));
			}
			
			File server = new File(doc, "server/javadoc");
			if (server.exists() && server.isDirectory()) {
				getLog().info("Repackaging server javadoc...");
				JarBuilder.build(server, new File(doc, "javadoc-server.jar"));				
			}
		} catch (IOException e) {
			throw new MojoExecutionException("Error preparing javadoc bundles", e);
		}

		//copy to a location convenient for bookmarking latest doc, etc
		try {
			getLog().info("Copying documentation to '" + latest.getAbsolutePath() + "' for convenience...");
			FileUtils.forceMkdir(latest);
			FileUtils.cleanDirectory(latest);
			FileUtils.copyDirectory(doc, latest, new NotFileFilter(new SuffixFileFilter(".jar")));
		} catch (IOException e) {
			throw new MojoFailureException("Unable to copy documentation to '" + latest.getAbsolutePath() + "'");
		}
		
		//allow custom POMS to be applied if necessary.  Useful for the guy that just has to have it before the 1st patch build. 
		FilenameFilter filter = new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.endsWith(".pom") || name.endsWith(".xml");
			}
		};
		if (unpacked.list(filter).length != 0 && !overwrite) {
			getLog().info("Directory contains at least one POM already, skipping pom extraction. "
						+ "Set overwrite.poms = true to change this behavior.");
			return;
		} 
		
		getLog().info("Extracting POMs...");
		try {
			PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(getClass().getClassLoader());
			Resource[] resources = resolver.getResources("/pom/*");
			
			for (Resource r : resources) {
				String content = IOUtils.toString(r.getInputStream());
				content = content.replaceAll("@version@", version);
				FileUtils.write(new File(unpacked, r.getFilename()), content);
			}
		} catch (Exception e) {
			throw new MojoExecutionException("Error extracting POMs from claspath resource", e);
		}
	}

	private void unpack(Archive source, ZipFile zip) throws IOException {

		AntPathMatcher matcher = new AntPathMatcher();

		Map<String, String> filter = source.getZipEntries();

		Enumeration<? extends ZipEntry> entries = zip.entries();
		while (entries.hasMoreElements()) {
			ZipEntry entry = entries.nextElement();
			if (entry.isDirectory()) {
				continue;
			}
			for (String s : filter.keySet()) {
				if (!matcher.match(s, entry.getName())) {
					continue;
				}
				String path = filter.get(s);
				if (matcher.isPattern(path)) {
					path = matcher.extractPathWithinPattern(filter.get(s), entry.getName());
				}
				File target = new File(unpacked, path);
				getLog().debug("Extracting to '" + target.getAbsolutePath() + "'");
				target.getParentFile().mkdirs();
				FileUtils.copyInputStreamToFile(zip.getInputStream(entry), target);
			}
		}
	}
	
}